package away3d.core.traverse ;


import away3d.cameras.Camera3D ;
import away3d.core.base.IRenderable ;
import away3d.core.data.RenderableListItem ;
import away3d.core.data.RenderableListItemPool ;
import away3d.core.math.Plane3D ;
import away3d.lights.LightBase ;
import away3d.materials.MaterialBase ;
import away3d.core.partition.NodeBase ;
import away3d.entities.Entity ;



/**
 * The EntityCollector class is a traverser for scene partitions that collects all scene graph entities that are
 * considered potientially visible.
 *
 * @see away3d.partition.Partition3D
 * @see away3d.partition.Entity
 */
class EntityCollector extends PartitionTraverser
{
    
    
    private  var _skyBox                    : IRenderable ;
    private  var _entities                  : Vector<Entity> ;
    private  var _opaqueRenderableHead      : RenderableListItem ;
    private  var _blendedRenderableHead     : RenderableListItem ;
    private  var _renderableListItemPool    : RenderableListItemPool ;
    private  var _lights                    : Vector<LightBase> ;
    private  var _numEntities               : Int ;
    private  var _numOpaques                : Int ;
    private  var _numBlended                : Int  ;
    private  var _numLights                 : Int  ;
    private  var _numTriangles              : Int  ;
    private  var _numMouseEnableds          : Int  ;
    private  var _camera                    : Camera3D ;
    
    
    
    /**
     * Creates a new EntityCollector object.
     */
    public function EntityCollector()
    {
        
        init() ;
        
    }
    
    
    private function init()
    {
//            _opaqueRenderables = new Vector<IRenderable>() ;
//            _blendedRenderables = new Vector<IRenderable>() ;
        _lights                 = new Vector() ;
        _entities               = new Vector() ;
        _renderableListItemPool = new RenderableListItemPool() ;
        
    }
    
    
    private function get_numOpaques() : Int
    {
        
        return _numOpaques ;
        
    }
    
    
    private function get_numBlended() : Int
    {
        
        return _numBlended ;
        
    }
    
    
    public var camera( get_camera, set_camera ) : Camera3D ;
    
    
    /**
     * The camera that provides the visible frustum.
     */
    private function get_camera() : Camera3D
    {
        
        return _camera ;
        
    }
    
    private function set_camera( value : Camera3D )
    {
        
        _camera = value ;
        
    }
    
    
    public var numMouseEnableds( get_nemMouseEnableds, null ) : Int
    
    
    /**
     * The amount of IRenderable objects that are mouse-enabled.
     */
    private function get_numMouseEnableds() : Int
    {
        
        return _numMouseEnableds ;
        
    }
    
    
    public var skyBox( get_skyBox, null ) : IRenderable ;
    
    
    /**
     * The sky box object if encountered.
     */
    private function get_skyBox() : IRenderable
    {
        
        return _skyBox ;
        
    }
    
    
    public var opaqueRenderableHead( get_opaqueRenderableHead, set_opaqueRenderableHead ) : RenderableListItem ;
    
    
    /**
     * The list of opaque IRenderable objects that are considered potentially visible.
     * @param value
     */
    private function get_opaqueRenderableHead() : RenderableListItem
    {
        
        return _opaqueRenderableHead ;
        
    }
    
    
    private function set_opaqueRenderableHead( value : RenderableListItem ) : RenderableListItem
    {
        
        _opaqueRenderableHead = value ;
        return value ;
        
    }
    
    
    public var blendedRenderableHead( get_blendedRenderableHead, set_blendedRenderableHead ) : RenderableListItem
    
    
    /**
     * The list of IRenderable objects that require blending and are considered potentially visible.
     * @param value
     */
    private function get_blendedRenderableHead() : RenderableListItem
    {
        
        return _blendedRenderableHead ;
        
    }
    
    
    private function set_blendedRenderableHead( value : RenderableListItem )
    {
        
        _blendedRenderableHead = value ;
        
    }
    
    
    public var lights( get_lights, null ): Vector<LightBase> ;
    
    
    /**
     * The lights of which the affecting area intersects the camera's frustum.
     */
    private function get_lights() : Vector<LightBase>
    {
        return _lights ;
    }

    /**
     * Clears all objects in the entity collector.
     * @param time The time taken by the last render
     * @param camera The camera that provides the frustum.
     */
    public function clear()
    {
        
        _numTriangles           = 0 ;
        _numMouseEnableds       = 0 ;
        _blendedRenderableHead  = null ;
        _opaqueRenderableHead   = null ;
        _renderableListItemPool.freeAll() ;
        
        if( _numLights > 0 ) _lights.length = _numLights = 0 ;
        
    }
    
    
    /**
     * Returns true if the current node is at least partly in the frustum. If so, the partition node knows to pass on the traverser to its children.
     *
     * @param node The Partition3DNode object to frustum-test.
     */
    override public function enterNode( node : NodeBase ) : Bool
    {
        
        return node.isInFrustum( _camera ) ;
        
    }
    
    
    /**
     * Adds a skybox to the potentially visible objects.
     * @param renderable The skybox to add.
     */
    override public function applySkyBox( renderable : IRenderable )
    {
        
        _skyBox = renderable ;
        
    }
    
    
    /**
     * Adds an IRenderable object to the potentially visible objects.
     * @param renderable The IRenderable object to add.
     */
    override public function applyRenderable( renderable : IRenderable )
    {
        
        var material : MaterialBase ;
        
        if( renderable.mouseEnabled ) ++_numMouseEnableds ;
        
        _numTriangles   += renderable.numTriangles ;
        material        = renderable.material ;
        
        if( material )
        {
            
            var item        = _renderableListItemPool.getItem() ;
            item.renderable = renderable ;
            item.materialId = material._uniqueId ;
            item.zIndex     = renderable.zIndex ;
            
            if( material.requiresBlending )
            {
                
                item.next               = _blendedRenderableHead ;
                _blendedRenderableHead  = item ;
                ++_numBlended ;
                
            }
            else
            {
                
                item.next               = _opaqueRenderableHead ;
                _opaqueRenderableHead   = item ;
                ++_numOpaques ;
                
            }
            
        }
        
    }
    
    
    /**
     * Adds a light to the potentially visible objects.
     * @param light The light to add.
     */
    override public function applyLight( light : LightBase )
    {
        
        _lights[ _numLights++ ] = light ;
        
    }
    
    
    /**
     * The total Float of triangles collected, and which will be pushed to the render engine.
     */
    private function get_numTriangles() : uint
    {
        
        return _numTriangles ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function applyEntity( entity : Entity )
    {
        
        _entities[ _numEntities++ ] = entity ;
        
    }
    
    
    /**
     * Cleans up any data at the end of a frame.
     */
    public function cleanUp()
    {
        
        if( _numEntities > 0 )
        {
            
            for ( i in 0..._numEntities )
            {
                
                _entities[ i ].popModelViewProjection() ;
                
            }
            
            _entities.length    = 0 ;
            _numEntities        = 0 ;
            
        }
        
    }
    
    
}